<?php
/**
 * Created by 99468
 * Date: 2017/8/14
 * Time: 20:26
 * Author: weblinuxgame
 * email : 994685563@qq.com
 * license : MIT
 **/
namespace EpgApp ;


defined('DS') or define('DS',DIRECTORY_SEPARATOR);
defined('EPG_APP_VENDOR_BOOT_LOAD_PATH') or define('EPG_APP_VENDOR_BOOT_LOAD_PATH',__DIR__.DS.'vendor'.DS.'autoload.php');

defined('EPG_APP_ROOT') or define('EPG_APP_ROOT',__DIR__);
defined('__EGP__ROOT__NAMESPACE__') or define('__EGP__ROOT__NAMESPACE__',__NAMESPACE__);

class  bootstrap
{
    protected static  $classMap ;
    protected static  $include_path ;
    protected static  $handlers ;
    private   static  $OK   ;
    protected static  $ext = array('.php','.phar','.ini','.phtml');

    const   AUTO_LOADER_FAILED  = -1;
    const   AUTO_LOADER_BEFORE  = 0 ;
    const   AUTO_LOADER_RESOLVE_BEFORE = 1;
    const   AUTO_LOADER_NOT_FIND = 2 ;
    const   AUTO_LOADER_SUCCESS  = 3 ;
    const   AUTO_LOADER_RET_ANY  = 4 ;
    const   AUTO_LOADER_INCLUDE_BEFORE  = 5 ;
    const   AUTO_LOADER_INIT_BEFORE  = 6 ;
    const   AUTO_LOADER_UNKNOWN_TARGET = 7 ;

    const  TARGET_LEVEL_TOP   = 0x001 ;
    const  TARGET_LEVEL_NEXT  = 0x010 ;
    const  TARGET_LEVEL_ONCE  = 0x100 ;

    public function __construct($classMap=array(),$include_path=array(),$handler=array())
    {
        self::$classMap = is_array($classMap)? $classMap : array() ;
        self::$include_path = is_array($include_path)? $include_path : array() ;
        $this->init();
    }

    public   function __autoload($className = '')
    {
        if(empty($className))
        {
            return false;
        }
        // 触发 解析前事件
        self::target(self::AUTO_LOADER_RESOLVE_BEFORE,array('class'=>$className));
        // 查找 类 | 函数 所在 文件位置
        $file = array(
              self::resolve($className,self::$include_path), // 直接解析 [应当 包括 命名空间解析 ]
              self::getClassFromMap($className),// Map 影射
              self::getClassFromAlias($className), // 别名 影射
            );
        for($i=0,$len=count($file);$i<$len;$i++)
        {
            if($file[$i])
            {
                return  self::loadFile($file[$i],array('class'=>$className,'classFile'=>$file[$i]));
            }
        }
        if(self::isOnlyLoader($this))
        {
           // echo 'is only one loader';
            self::target(self::AUTO_LOADER_RET_ANY,array('class'=>$className));
        }
        return false;
    }

    protected function init()
    {
        // 自动加 vendor bootstrap
        self::target(self::AUTO_LOADER_INIT_BEFORE);
        return $this->vendorAutoloadRegister(EPG_APP_VENDOR_BOOT_LOAD_PATH);
    }

    public static function import($file='',$try_dir=array())
    {
        if(empty($file))
        {
            return false;
        }
        if(file_exists($file))
        {
            return include($file);
        }
        if(self::isClassName($file))
        {
            $tmp = self::resolve($file,$try_dir);
            if($tmp)
            {
              return include($tmp);
            }
        }

        return false ;
    }

    private static  function handler($id,$args)
    {
        if(empty(self::$handlers))
        {
            self::$handlers = array();
            return ;
        }
        $_list = isset(self::$handlers[$id]) ? self::$handlers[$id] : array();
        $len   = count($_list);
        for($i=0;$i<$len;)
        {
            $handler = isset($_list[$i]['handler']) ? $_list[$i]['handler'] : false ;
            if( $handler instanceof  \Closure)
            {
                $_list[$i]($args);
            }
            if( $handler && isset($_list[$i]['once']) && $_list[$i]['once'])
            {
                array_splice($_list, $i, 1);
                --$len;
                break;
            }
            $i++;

        }
    }

    private static function loadFile($file,$args=array())
    {

            self::target(self::AUTO_LOADER_INCLUDE_BEFORE,$args);
            $ret = self::import($file);
            if($ret)
            {
                self::target(self::AUTO_LOADER_SUCCESS,$args);
                return true;
            }
            else
            {
                self::target(self::AUTO_LOADER_FAILED,array('class'=>$className,'classFile'=>$tmp));
            }
        return false;
    }

    public function registerResolveHandler($eventTarget,\Closure $handler,$level=0)
    {
        $eventTargets = self::getTargetConst() ;
        $flag         = false;
        $id           = null ;
        foreach($eventTargets as $key=>$value)
        {
                if($eventTarget === $key || $eventTarget === $value)
                {
                    $flag = true ;
                    $id   = $value ;
                    break ;
                }
        }
        if($flag)
        {
            if(empty(self::$handlers))
            {
                self::$handlers = array();
            }
            if(!isset(self::$handlers[$id]))
            {
                self::$handlers[$id] =array();
            }
            if(empty(self::$handlers[$id]))
            {
                if($handler instanceof  \Closure)
                {
                    self::$handlers[$id][] = $handler ;
                }
            }
            else
            {   if($handler instanceof  \Closure)
                {
                    if($level === self::TARGET_LEVEL_TOP)
                    {
                        array_unshift(self::$handlers[$id],array('handler'=>$handler));
                    }
                    if($level === self::TARGET_LEVEL_NEXT )
                    {
                        array_push(self::$handlers[$id],array('handler'=>$handler));
                    }
                    if($level === self::TARGET_LEVEL_ONCE )
                    {
                        array_push(self::$handlers[$id],array('handler'=>$handler,'once'=>true));
                    }
                }
            }
        }
    }

    public function registerSpl($vendor=array())
    {
        if(!empty($vendor))
        {
            if(is_array($vendor))
            {
                $self = $this;
                array_map(function($item)use($self){
                    $self::vendorAutoloadRegister($item);
                },$vendor);
            }
            if(is_string($vendor))
            {
                self::vendorAutoloadRegister($vendor);
            }
        }
        if(empty(self::$OK))
        {
            if(spl_autoload_register(array($this,'__autoload')))
            {
                self::$OK = true;
                return $this;
            }
        }
        else if(self::$OK)
        {
            return $this;
        }
        return false;
    }

    public static function addClassNameSpace($namespace,$root_dir)
    {
            if(empty($namespace) || empty($root_dir) || !is_dir($root_dir))
            {
                return false;
            }
            $Map = self::getClassFromMap();
            if(empty($Map))
        {
            throw new Exception('classMap is empty or not init!');
        }
            if(!isset($Map['#NameSpace']))
            {
                $Map['#NameSpace'] =array();
            }
            if(isset($Map['#NameSpace'][$namespace]))
            {
                return false;
            }
            $Map['#NameSpace'][$namespace] = $root_dir ;
            return true;
    }

    protected static  function getClassMapByNameSpace($className='')
    {
        if(empty($className))
        {
            return false;
        }
        $Map = self::getClassFromMap();
        if(empty($Map))
        {
            throw new Exception('classMap is empty or not init!');
        }
        if(!isset($Map['#NameSpace']))
        {
            return false;
        }
        $namespace = explode('\\',$className);
        if(!empty($namespace)&& isset($Map['#NameSpace'][$namespace[0]]))
        {
            $path  = $Map['#NameSpace'][$namespace[0]] ;
            $file  = $path . DS . str_replace('\\',DS,$className);
            for($i=0,$len=count(self::$ext);$i<$len;$i++)
            {
                $tmp = $file.self::$ext[$i];
                if(file_exists($tmp))
                {
                    return $tmp;
                }
            }
        }
        return false ;
    }

    protected static  function getClassFromMap($className='')
    {
        if(!isset(self::$classMap))
        {
            self::$classMap = array();
        }
        if(empty($className))
        {
            return self::$classMap ;
        }
        if(is_string($className) && isset(self::$classMap[$className]))
        {
            return self::$classMap[$className];
        }
        return false;
    }

    public static function setClassMap($className='',$filePath='')
    {
        if(empty($className) || empty($filePath) || !file_exists($filePath))
        {
            return false;
        }
        $Map = self::getClassFromMap();
        if(empty($Map))
        {
           throw new Exception('classMap is empty or not init!');
        }
        if(isset($Map[$className]))
        {
           return false;
        }
        $Map[$className] = $filePath ;
        return true ;
    }

    public static function setClassAlias($name,$filePath='')
    {
        if(empty($name) || empty($filePath) || !file_exists($filePath))
        {
            return false;
        }
        $Map = self::getClassFromMap();
        if(empty($Map))
        {
            throw new Exception('classMap is empty or not init!');
        }
        if(!isset($Map['@']))
        {
            $Map['@'] = array();
        }
        $aliasMap = $Map['@'];
        if(isset($aliasMap[$name]))
        {
            return false;
        }
        $aliasMap[$name] = $filePath ;
        return true;
    }

    protected static function getClassFromAlias($aliasName)
    {
        if(empty($aliasName))
        {
            return false;
        }
        $Map = self::getClassFromMap();
        if(empty($Map))
        {
            throw new Exception('classMap is empty or not init!');
        }
        if(isset($Map['@']) && !empty($Map['@']) && is_array($Map['@']) && isset($Map['@'][$aliasName]))
        {
            return $Map['@'][$aliasName];
        }
        return false;
    }

    protected static function isOnlyLoader($self)
    {
        $loaders = spl_autoload_functions() ;
        if(empty($loaders))
        {
             (new self())->registerSpl();
            return true;
        }
        if(count($loaders)>1)
        {
            return true;
        }
        if(empty($self) || !is_object($self))
        {
            if($loaders[0][0] instanceof bootstrap)
            {
                return true;
            }
            return false;
        }
        if($loaders[0][0] == $self)
        {
            return true;
        }
        return false;
    }

    public static function resolve($className,$basePath='')
    {
            if(empty($className)||!is_string($className))
            {
                return false;
            }
            $file = str_replace('\\',DS,$className);
            for($i=0,$len=count(self::$ext);$i<$len;$i++)
            {
                    $tmp = $file.self::$ext[$i] ;
                    if(file_exists($tmp))
                    {
                        return $tmp ;
                    }
                    if(!empty($basePath))
                    {
                        if(is_string($basePath))
                        {
                            $tmp = $basePath.DS.$tmp ;
                            if(file_exists($tmp))
                            {
                                return $tmp ;
                            }
                        }
                        if(is_array($basePath))
                        {
                            for($j=0,$_len=count($basePath);$j<$_len;$j++)
                            {
                                $tmp = self::resolve($className,$basePath[$j]);
                                if($tmp)
                                {
                                    return $tmp;
                                }
                            }
                        }
                    }
                    $tmp = __DIR__.DS.$file.self::$ext[$i];
                    if(file_exists($tmp))
                    {
                        return $tmp;
                    }
                    $tmp = dirname(__DIR__).DS.$file.self::$ext[$i];
                    if(file_exists($tmp))
                    {
                         return $tmp;
                    }
            }
            $tmp = self::getClassMapByNameSpace($className);
            if($tmp)
            {
                return $tmp;
            }

        return false;
    }

    public static function isClassName($class='')
    {
        if(empty($class)&&!is_string($class))
        {
            return false;
        }
        $pattern = '/^(.+|\\\.+)\\\\w$/';
        if(preg_match($pattern,$class))
        {
            return true ;
        }
        return false;
    }

    public static function addFileExtSupport($ext='')
    {
        if(empty($ext))
        {
            return false;
        }
        if(is_string($ext))
        {
            for($i=0,$len=count(self::$ext);$i<$len;$i++)
            {
                if($ext==self::$ext[$i])
                {
                    return false ;
                }
            }
            self::$ext[] = $ext;
        }
        if(is_array($ext))
        {
            for ($i=0,$len=count($ext);$i<$len;$i++)
            {
                self::addFileExtSupport($ext[$i]);
            }
        }
        return false;
    }

    protected static function  target($flag,$args=array())
    {
        if(empty($flag)&&!is_numeric($flag))
        {
            $flag = self::AUTO_LOADER_UNKNOWN_TARGET ;
        }
        if($flag>self::AUTO_LOADER_UNKNOWN_TARGET || $flag<self::AUTO_LOADER_FAILED)
        {
            $flag = self::AUTO_LOADER_UNKNOWN_TARGET ;
        }
        self::handler($flag,$args);
    }

    public function vendorAutoloadRegister($vendor)
    {
        if(file_exists($vendor))
        {
            include($vendor);
            self::target(self::AUTO_LOADER_SUCCESS,array('class'=>'vendorAutoloader','classFile'=>$vendor,'id'=>time()));
            return true ;
        }
        self::target(self::AUTO_LOADER_FAILED,array('class'=>'vendorAutoloader','classFile'=>$vendor,'id'=>time()));
        return false;
    }

    public static function getTargetConst()
    {
        return array(
            'AUTO_LOADER_FAILED'=>self::AUTO_LOADER_FAILED,
            'AUTO_LOADER_BEFORE'=>self::AUTO_LOADER_BEFORE,
            'AUTO_LOADER_RESOLVE_BEFORE'=>self::AUTO_LOADER_RESOLVE_BEFORE,
            'AUTO_LOADER_NOT_FIND'=>self::AUTO_LOADER_NOT_FIND,
            'AUTO_LOADER_SUCCESS'=>self::AUTO_LOADER_SUCCESS,
            'AUTO_LOADER_RET_ANY'=>self::AUTO_LOADER_RET_ANY,
            'AUTO_LOADER_INCLUDE_BEFORE'=>self::AUTO_LOADER_INCLUDE_BEFORE,
            'AUTO_LOADER_INIT_BEFORE'=>self::AUTO_LOADER_INIT_BEFORE,
            'AUTO_LOADER_UNKNOWN_TARGET'=>self::AUTO_LOADER_UNKNOWN_TARGET,
        );
    }

    public static function dumpClassMap()
    {
        return var_dump(self::$classMap);
    }

    public static function dumpIncludePath()
    {
        return var_dump(self::$include_path);
    }



}

